// Copyright (c) 2024 CHANGLEI. All rights reserved.

import 'package:charts_grasp/src/chart/common/data_label.dart';
import 'package:charts_grasp/src/chart/utils/enum.dart';
import 'package:charts_grasp/src/circular_chart/renderer/chart_point.dart';
import 'package:charts_grasp/src/circular_chart/renderer/renderer_base.dart';
import 'package:charts_grasp/src/circular_chart/renderer/renderer_extension.dart';
import 'package:charts_grasp/src/circular_chart/utils/enum.dart';
import 'package:charts_grasp/src/common/common.dart';
import 'package:charts_grasp/src/common/series/chart_series.dart';
import 'package:charts_grasp/src/common/user_interaction/selection_behavior.dart';
import 'package:charts_grasp/src/common/utils/enum.dart';
import 'package:charts_grasp/src/common/utils/typedef.dart';
import 'package:flutter/material.dart';

class CircularSeries<T, D> extends ChartSeries<T, D> implements CircularChartEmptyPointBehavior {
  CircularSeries({
    this.key,
    this.onCreateRenderer,
    this.onRendererCreated,
    this.onPointTap,
    this.onPointDoubleTap,
    this.onPointLongPress,
    this.dataSource,
    this.xValueMapper,
    this.yValueMapper,
    this.pointColorMapper,
    this.pointShaderMapper,
    this.pointRadiusMapper,
    this.dataLabelMapper,
    this.sortFieldValueMapper,
    int? startAngle,
    int? endAngle,
    String? radius,
    String? innerRadius,
    bool? explode,
    bool? explodeAll,
    this.explodeIndex,
    ActivationMode? explodeGesture,
    String? explodeOffset,
    this.groupTo,
    this.groupMode,
    this.pointRenderMode,
    String? gap,
    double? opacity,
    EmptyPointSettings? emptyPointSettings,
    Color? borderColor,
    double? borderWidth,
    DataLabelSettings? dataLabelSettings,
    bool? enableTooltip,
    this.name,
    double? animationDuration,
    double? animationDelay,
    SelectionBehavior? selectionBehavior,
    SortingOrder? sortingOrder,
    LegendIconType? legendIconType,
    CornerStyle? cornerStyle,
    List<int>? initialSelectedDataIndexes,
  })  : startAngle = startAngle ?? 0,
        animationDuration = animationDuration ?? 1500,
        animationDelay = animationDelay ?? 0,
        endAngle = endAngle ?? 360,
        radius = radius ?? '80%',
        innerRadius = innerRadius ?? '50%',
        explode = explode ?? false,
        explodeAll = explodeAll ?? false,
        explodeOffset = explodeOffset ?? '10%',
        explodeGesture = explodeGesture ?? ActivationMode.singleTap,
        gap = gap ?? '1%',
        cornerStyle = cornerStyle ?? CornerStyle.bothFlat,
        dataLabelSettings = dataLabelSettings ?? const DataLabelSettings(),
        emptyPointSettings = emptyPointSettings ?? EmptyPointSettings(),
        selectionBehavior = selectionBehavior ?? SelectionBehavior(),
        borderColor = borderColor ?? Colors.transparent,
        borderWidth = borderWidth ?? 0.0,
        opacity = opacity ?? 1,
        enableTooltip = enableTooltip ?? true,
        sortingOrder = sortingOrder ?? SortingOrder.none,
        legendIconType = legendIconType ?? LegendIconType.seriesType,
        initialSelectedDataIndexes = initialSelectedDataIndexes ?? <int>[],
        super(name: name);

  @override
  // ignore: overridden_fields
  final double opacity;

  @override
  // ignore: overridden_fields
  final DataLabelSettings dataLabelSettings;

  @override
  // ignore: overridden_fields
  final List<T>? dataSource;

  @override
  // ignore: overridden_fields
  final ChartIndexedValueMapper<D>? xValueMapper;

  @override
  // ignore: overridden_fields
  final ChartIndexedValueMapper<num>? yValueMapper;

  @override
  // ignore: overridden_fields
  final ChartIndexedValueMapper<Color>? pointColorMapper;

  final ChartShaderMapper<dynamic>? pointShaderMapper;

  final ChartIndexedValueMapper<String>? pointRadiusMapper;

  @override
  // ignore: overridden_fields
  final ChartIndexedValueMapper<String>? dataLabelMapper;

  @override
  // ignore: overridden_fields
  final ChartIndexedValueMapper<dynamic>? sortFieldValueMapper;

  @override
  // ignore: overridden_fields
  final LegendIconType legendIconType;

  @override
  // ignore: overridden_fields
  final SortingOrder sortingOrder;

  @override
  // ignore: overridden_fields
  final bool enableTooltip;

  @override
  // ignore: overridden_fields
  final double borderWidth;

  @override
  // ignore: overridden_fields
  final Color borderColor;

  @override
  // ignore: overridden_fields
  final EmptyPointSettings emptyPointSettings;

  @override
  // ignore: overridden_fields
  final SelectionBehavior selectionBehavior;

  final int startAngle;

  final int endAngle;

  final String radius;

  final String innerRadius;

  final bool explode;

  final bool explodeAll;

  final int? explodeIndex;

  final String explodeOffset;

  final ActivationMode explodeGesture;

  final double? groupTo;

  final CircularChartGroupMode? groupMode;

  final PointRenderMode? pointRenderMode;

  final String gap;

  final CornerStyle cornerStyle;

  @override
  // ignore: overridden_fields
  final String? name;

  @override
  // ignore: overridden_fields
  final double animationDuration;

  @override
  // ignore: overridden_fields
  final double animationDelay;

  List<int> initialSelectedDataIndexes;

  final ValueKey<String>? key;

  final ChartSeriesRendererFactory<T, D>? onCreateRenderer;

  final ChartPointInteractionCallback? onPointTap;

  final ChartPointInteractionCallback? onPointDoubleTap;

  final ChartPointInteractionCallback? onPointLongPress;

  final CircularSeriesRendererCreatedCallback? onRendererCreated;

  @override
  void calculateEmptyPointValue(
    int pointIndex,
    ChartPoint<dynamic> currentPoint, [
    CircularSeriesRenderer? seriesRenderer,
  ]) {
    final EmptyPointSettings empty = emptyPointSettings;
    final List<ChartPoint<dynamic>>? dataPoints = (seriesRenderer as CircularSeriesRendererExtension?)?.dataPoints;
    final int pointLength = dataPoints!.length;
    final ChartPoint<dynamic> point = dataPoints[pointIndex];
    if (point.y == null) {
      switch (empty.mode) {
        case EmptyPointMode.average:
          final num previous = pointIndex - 1 >= 0 ? dataPoints[pointIndex - 1].y ?? 0 : 0;
          final num next = pointIndex + 1 <= pointLength - 1 ? dataPoints[pointIndex + 1].y ?? 0 : 0;
          point.y = (previous + next).abs() / 2;
          point.isVisible = true;
          point.isEmpty = true;
          break;
        case EmptyPointMode.zero:
          point.y = 0;
          point.isVisible = true;
          point.isEmpty = true;
          break;
        case EmptyPointMode.drop:
        case EmptyPointMode.gap:
          point.isEmpty = true;
          point.isVisible = false;
          break;
      }
    }
  }
}

int? getVisiblePointIndex(List<ChartPoint<dynamic>?> points, String loc, int index) {
  if (loc == 'before') {
    for (int i = index; i >= 0; i--) {
      if (points[i - 1]!.isVisible) {
        return i - 1;
      }
    }
  } else {
    for (int i = index; i < points.length; i++) {
      if (points[i + 1]!.isVisible) {
        return i + 1;
      }
    }
  }
  return null;
}
